﻿//   Interpreter.cs
//
//   Copyright 2009 Eric Llewellyn
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LibEELbeta
{
    public class Interpreter
    {
        public Scanner scanner = new Scanner();
        public Parser parser = new Parser();
        public CodeGenerator codeGenerator = new CodeGenerator();
        public BinaryConverter binaryConverter = new BinaryConverter();
        public VirtualMachine virtualMachine = new VirtualMachine();

        public event SyntaxErrorDelegate SyntaxError;
        public event RuntimeErrorDelegate RuntimeError;

        public ReadDelegate ReadFunction
        {
            get { return virtualMachine.ReadFunction; }
            set { virtualMachine.ReadFunction = value; }
        }

        public WriteDelegate WriteFunction
        {
            get { return virtualMachine.WriteFunction; }
            set { virtualMachine.WriteFunction = value; }
        }

        public bool WaitingForInput
        {
            get { return parser.WaitingForInput; }
        }

        public Interpreter()
        {
            parser.SyntaxError += new SyntaxErrorDelegate(parser_SyntaxError);
            virtualMachine.RuntimeError += new RuntimeErrorDelegate(virtualMachine_RuntimeError);
        }

        public void RunText(string input)
        {
            Token[] tokens = scanner.ScanLine(input);
            SyntaxNode[] syntaxNodes = parser.ParseTokens(tokens);
            Instruction[] instructions = codeGenerator.GetInstructions(syntaxNodes);

            virtualMachine.LoadProgram(instructions);
            virtualMachine.Run();
        }

        public bool RunBinary(byte[] input)
        {
            if (input[0] != 24)
                return false;

            Instruction[] instructions = binaryConverter.BinaryToInstructions(input);
            
            virtualMachine.LoadProgram(instructions);
            virtualMachine.Run();

            return true;
        }

        public byte[] Compile(string input)
        {
            Token[] tokens = scanner.ScanLine(input);
            SyntaxNode[] syntaxNodes = parser.ParseTokens(tokens);
            Instruction[] instructions = codeGenerator.GetInstructions(syntaxNodes);
            byte[] output = binaryConverter.InstructionsToBinary(instructions);

            return output;
        }

        public object Get(string name)
        {
            object result;

            virtualMachine.Push(name);
            if (name.StartsWith("$"))
                virtualMachine.GLoad();
            else
                virtualMachine.Load();
            result = virtualMachine.Pop();

            return result;
        }

        public void Set(string name, object value)
        {
            virtualMachine.Push(value);
            virtualMachine.Push(name);
            if (name.StartsWith("$"))
                virtualMachine.GStore();
            else
                virtualMachine.Store();
        }

        public object Call(object function, params object[] args)
        {
            object result;

            foreach (object o in args)
                virtualMachine.Push(o);

            virtualMachine.Push(function);

            virtualMachine.Call();
            result = virtualMachine.Pop();

            return result;
        }

        void parser_SyntaxError(string msg, Token t)
        {
            SyntaxError.Invoke(msg, t);
        }

        void virtualMachine_RuntimeError(string message, int instructionNumber)
        {
            RuntimeError.Invoke(message, instructionNumber);
        }
    }
}
